package jcu.salt.io;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import jcu.sal.common.cml.CMLConstants;
import jcu.salt.Configuration;

/**
 * Provides convenient access to a data turbine connection
 * to send a transmittable to the ring buffer.
 * 
 * @author SAL-T team
 *
 */

public class Sender {

	private String turbineIP;
	private String turbineSource;
	private String turbineChannel;
	private DataTurbineWriteConnector dtcon;
	private State state;

	public Sender(String ip, String source, String channel) {
		state = new State();
		turbineIP = ip;
		turbineSource = source;
		turbineChannel = channel;
	}

	public void connect() {
		if(state.connect()){
			dtcon = new DataTurbineWriteConnector(turbineIP, turbineSource,
				turbineChannel);
			state.commit();
		}
	}

	public void disconnect() {
		try {
			state.disconnect();
			dtcon.close();
			dtcon = null;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * store a given {@link Transmittable} object into the ring buffer
	 * by use of the {@link TransmissionOutputStream}
	 * @param t
	 */
	public boolean send(Transmittable t) {
		if (state.get()) {
			boolean ret = false;
			Configuration config = Configuration.getInstance();

			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			TransmissionOutputStream out = new TransmissionOutputStream(bos,
					config.isSet(Configuration.ENCODED));


			try {
				out.write(t);
				out.flush();
				out.close();
				dtcon.write(bos.toByteArray());
				ret = true;
			} catch (IOException e) {
				// TODO throw new exception here ?!?
				System.err.println("failed to encode/write the data");
				e.printStackTrace();
			} finally {
				state.put();
			}
			return ret;
		}
		else 
			return false;
	}

	/**
	 * packs the given {@link String} into a {@link Transmittable} and send
	 * it to the ring buffer 
	 * @param data
	 */
	public boolean send(String data) {
		Transmittable t = new Transmittable("Send Test SAL-T", 0,
				CMLConstants.RET_TYPE_STRING, data.getBytes(), System
						.currentTimeMillis());
		return send(t);
	}

	private static class State {
		private static final int DISCONNECTED = 0;
		private static final int CONNECTED = 1;
		private int users = 0; 
		private int state = DISCONNECTED, tmp=DISCONNECTED;
		
		public State(){
			state = DISCONNECTED;
			users = 0;
		}
		
		public synchronized boolean connect(){
			if(state==DISCONNECTED){
				tmp=CONNECTED;
				return true;
			} else
				return false;
		}
		
		public synchronized void commit(){
			state = tmp;
		}
		
		public synchronized void disconnect() throws InterruptedException{
			if(state==CONNECTED && users==0){
				state=DISCONNECTED;
			} else if (state==CONNECTED && users!=0){
				wait();
				state=DISCONNECTED;
			}
		}
		
		public synchronized boolean get(){
			if(state==CONNECTED){
				users++;
				return true;
			} else
				return false;
		}
		
		public synchronized boolean put(){
			if(state==CONNECTED){
				if(--users==0)
					notify();				
				return true;
			} else
				return false;
		}
	}
}
